#include "parsers.h"
#include "HelperFunctions.h"

void parse_PROGRAM()
{
	TokenType followList[] = { EOF_Token, none_Token };

	if (match(main_Token, followList))
	{		
		outputRule(1);
		parse_DEFINITIONS();
		if (match(begin_Token, followList))
		{
			parse_COMMANDS();
			if (match(end_Token, followList))
			{
				match(EOF_Token, NULL);
			}
		}
	}
}

void parse_DEFINITIONS()
{
	outputRule(2);
	parse_DEFINITION();
	parse_DEFINITIONS_i();
}

void parse_DEFINITIONS_i()
{
	TokenType followList[] = { begin_Token, none_Token };
	Token *t = nextToken();

	if (t->type == semicolon_Token)
	{
		outputRule(3);
		parse_DEFINITION();
		parse_DEFINITIONS_i();
	}
	else
	{
		outputRule(4);
		backToken();
	}
}

void parse_DEFINITION()
{
	TokenType followList[] = { semicolon_Token, begin_Token, none_Token };

	if (match(id_Token, followList))
	{
		if (match(colon_Token, followList))
		{
			outputRule(5);
			parse_DEFINITION_i();
		}
	}
}

void parse_DEFINITION_i()
{
	TokenType followList[] = { semicolon_Token, begin_Token, none_Token };
	Token *t = nextToken();

	if (t->type == exception_Token)
	{
		outputRule(6);
	}
	else if (attemptRepairKeyword(t, exception_Token))
	{
		TokenType expectedTokenTypes[] = { exception_Token, none_Token };

		backToken();
		outputWarning(t, expectedTokenTypes);
		parse_DEFINITION_i();
	}
	else
	{
		outputRule(7);
		backToken();
		parse_TYPE();
		parse_DEFINITION_ii();
	}
}

void parse_DEFINITION_ii()
{
	TokenType followList[] = { semicolon_Token, begin_Token, none_Token };
	Token *t = nextToken();

	if (t->type == const_Token)
	{
		outputRule(8);
		if (match(assign_Token, followList))
		{
			if (match(number_Token, followList))
			{
			}
		}
	}
	else if (attemptRepairKeyword(t, const_Token))
	{
		TokenType expectedTokenTypes[] = { const_Token, none_Token };

		backToken();
		outputWarning(t, expectedTokenTypes);
		parse_DEFINITION_ii();
	}
	else
	{
		outputRule(9);
		backToken();
	}
}

void parse_TYPE()
{
	TokenType followList[] = { const_Token, semicolon_Token, begin_Token, none_Token };
	TokenType expectedTokenTypes[] = { integer_Token, real_Token, none_Token };
	Token *t = nextToken();

	switch(t->type)
	{
	case integer_Token:
		outputRule(10);
		break;
	case real_Token:
		outputRule(11);
		break;
	default:
		if (attemptRepairKeyword(t, integer_Token) | attemptRepairKeyword(t, real_Token))
		{
			backToken();
			outputWarning(t, expectedTokenTypes);
			parse_TYPE();
		}
		else
		{
			outputError(t, expectedTokenTypes);
			gotoFollow(followList);
		}

		break;
	}
}

void parse_COMMANDS()
{
	TokenType followList[] = { end_Token, else_Token, fi_Token, until_Token, none_Token };
	Token *t = nextToken();

	// Must start with ';', so no error recovery here.
	if (t->type == semicolon_Token)
	{
		outputRule(12);
		parse_COMMAND();
		parse_COMMANDS();
	}
	else
	{		
		outputRule(13);
		backToken();
	}
}

void parse_COMMAND()
{
	TokenType expectedTokenTypes[] = { id_Token, if_Token, do_Token, throw_Token, none_Token };
	TokenType followList[] = { semicolon_Token, end_Token, else_Token, fi_Token, until_Token, none_Token };
	Token *t = nextToken();

	switch(t->type)
	{
	case id_Token:
		outputRule(14);
		if (match(assign_Token, followList))
		{
			parse_EXPRESSION();
		}
		break;
	case if_Token:
		outputRule(15);
		parse_CONDITION();
		if (match(then_Token, followList))
		{
			parse_COMMANDS();
			if (match(else_Token, followList))
			{
				parse_COMMANDS();
				match(fi_Token, followList);
			}
		}
		break;
	case do_Token:
		outputRule(16);
		parse_COMMANDS();
		if (match(until_Token, followList))
		{			
			parse_CONDITION();
			match(od_Token, followList);
		}
		break;
	case throw_Token:
		outputRule(17);
		match(id_Token, followList);
		break;
	default:
		if (attemptRepairKeyword(t, if_Token) | attemptRepairKeyword(t, do_Token) | attemptRepairKeyword(t, throw_Token))
		{
			TokenType expectedTokenTypes[] = { if_Token, do_Token, throw_Token, none_Token };

			backToken();
			outputWarning(t, expectedTokenTypes);
			parse_COMMAND();
		}
		else
		{
			outputError(t, expectedTokenTypes);
			gotoFollow(followList);
		}

		break;
	}
}

void parse_EXPRESSION()
{
	TokenType followList[] = { rel_op_Token, then_Token, od_Token, rightbracket_Token, ar_op_Token, 
		end_Token, else_Token, fi_Token, until_Token, none_Token };
	TokenType expectedTokenTypes[] = { number_Token, id_Token, leftbracket_Token, none_Token };
	Token *t = nextToken();

	switch(t->type)
	{
	case number_Token:
		outputRule(18);
		parse_EXPRESSION_i();
		break;
	case id_Token:
		outputRule(19);
		parse_EXPRESSION_i();
		break;
	case leftbracket_Token:
		outputRule(20);
		parse_EXPRESSION();
		if (match(rightbracket_Token, followList))
		{
			parse_EXPRESSION_i();
		}
		break;
	default:
		outputError(t, expectedTokenTypes);
		gotoFollow(followList);
		break;
	}
}

void parse_EXPRESSION_i()
{
	TokenType followList[] = { rel_op_Token, then_Token, od_Token, rightbracket_Token, ar_op_Token, 
		end_Token, else_Token, fi_Token, until_Token, none_Token };
	Token *t = nextToken();

	if (t->type == ar_op_Token)
	{
		outputRule(21);
		parse_EXPRESSION();
		parse_EXPRESSION_i();
	}
	else
	{
		outputRule(22);
		backToken();
	}
}

void parse_CONDITION()
{
	TokenType followList[] = { then_Token, od_Token, none_Token };

	outputRule(23);
	parse_EXPRESSION();
	if (match(rel_op_Token, followList))
	{
		parse_EXPRESSION();
	}
}